package tomasulo;
import java.io.*;
import java.util.*;

public class Kernel{
	static final int DEFAULT_MILLISEC = 1000;

	static final int READY = 0;
	static final int RUNNING = 1;
	static final int PAUSED = 2;

	static Kernel kernel; // Kernel实例，供外部调用

	Timer timer; // 定时器
	int delay;
	class Clicker extends TimerTask {
		Kernel kernel;

		@Override
		public void run() {
			kernel.click();
		}

		Clicker(Kernel kernel) {
			this.kernel = kernel;
		}
	};
	Clicker clicker;
	
	Register register;
	Memory memory;
	Execute add, mul;
	InstructionManager manager;
	int cycle; // CPU正在执行第cycle个时钟周期：0^111_^222_^...，其中^表示Click，_表示统一dispatch
	int state; // 整个模拟器状态（就绪、运行、暂停）

	public void kstart() { // 开始
		System.out.println("<Start>");
		timer = new Timer();
		clicker = new Clicker(this);
		timer.schedule(clicker, delay, delay);
		state = RUNNING;
	}

	public void kpause() { // 暂停
		System.out.println("<Pause>");
		if(clicker != null) clicker.cancel();
		state = PAUSED;
	}

	public void kstop() { // 停止
		System.out.println("<Stop>");
		if(clicker != null) clicker.cancel();
		clear();
		state = READY;
	}

	public void kstep() { // 单步
		System.out.println("<Step>");
		if(clicker != null) clicker.cancel();
		state = RUNNING;
		click();
		state = PAUSED;
	}

	public void setMem(int addr, double val) {
		memory.val[addr] = val;
	}
	
	public void setReg(int no, double val) {
		register.val[no] = val;
	}
	
	public void setDelay(int delay) { // 设置周期长度：单位千分之一秒
		if (state == RUNNING) {
			if(clicker != null) clicker.cancel();
			timer = new Timer();
			clicker = new Clicker(this);
			timer.schedule(clicker, this.delay, delay);
		}
		this.delay = delay;
	}

	LinkedList<Integer> waitStationNo;
	LinkedList<Double> waitVal;

	public void dispatch(int stationNo, double val) { // 在下个周期开始前分发站号的值
		waitStationNo.addLast(stationNo);
		waitVal.addLast(val);
	}

	public void flushpatch() { // 在一个周期结束时调用，各部件dispatch是立即分发
		while (!waitVal.isEmpty()) {
			int stationNo = waitStationNo.pollFirst();
			double val = waitVal.pollFirst();
			System.out.println("[Kernel.flushpatch]\nstationNo="+stationNo+" val="+val);
			register.dispatch(stationNo, val);
			memory.dispatch(stationNo, val);
			add.dispatch(stationNo, val);
			mul.dispatch(stationNo, val);
		}
	}

	private void clear() {
		manager.clear();
		register.clear();
		memory.clear();
		add.clear();
		mul.clear();
		cycle = 0;
		state = READY;
		
		waitStationNo.clear();
		waitVal.clear();
	}

	private void click() {
		System.out.println("\n(Cycle " + cycle+")");
		cycle++;
		
		manager.click();
		memory.click();
		add.click();
		mul.click();
		flushpatch();
		
		//DEBUG
		System.out.print("\nMem0~10: ");
		for (int i=0;i<10;i++) System.out.print(kernel.memory.val[i]+" ");
		System.out.println();
		System.out.print("Reg0~8: ");
		for (int i=0;i<8;i++) System.out.print(kernel.register.val[i]+" ");
		System.out.println();
		System.out.print("RegSt0~8: ");
		for (int i=0;i<8;i++) System.out.print(kernel.register.stationNo[i]+" ");
		System.out.println();
		//END

		TomasuloApp.uupdate();
	}
	
	public int loadFile(String fileName) {
                kernel.clear();
		String s;
		FileInputStream is;
		InputStreamReader ir;
		BufferedReader in;
		try {
			is = new FileInputStream(fileName);
			ir = new InputStreamReader(is);
			in = new BufferedReader(ir);
			while ((s = in.readLine()) != null) {
                                System.out.println("addInstrucion" +s);
				kernel.manager.addInstruction(s);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return -1;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("File \""+fileName+"\" loaded");
		return 0;
	}
	
	Kernel() {
		delay = DEFAULT_MILLISEC;
		register = new Register();
		memory = new Memory();
		add = new Add();
		mul = new Mul();
		manager = new InstructionManager();
		cycle = 0;
		state = READY;
		
		waitStationNo = new LinkedList<Integer>();
		waitVal = new LinkedList<Double>();
		
		clear();
	}

	/*public static void main(String args[]) {
		System.out.println("Tomasulo simulator!\n\n");
		//DEBUG
		kernel = new Kernel();
		kernel.setMem(0, 11);
		kernel.setMem(1, 33);
		kernel.setMem(2, 22);
		kernel.setMem(3, 44);
		kernel.loadFile("T03.txt");
		//END
		kernel.kstart();
	}*/
}